// 一
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello')
    }, 10)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('lagou')
    }, 10)
})
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('i love you')
    }, 10)
})
Promise.all([p1, p2, p3]).then((vals) => {
    console.log(vals.join(' '))
})

p1.then(a => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            var b = `${a} lagou`
            resolve(b)
        }, 10)
    })
}).then(b => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            var c = `${b} i love you`
            resolve(c)
        }, 10)
    })
}).then(v => {
    console.log(v)
})

// 二
const _ = require('lodash')
const fp = require('lodash/fp')
const { noop, reject } = require('lodash')
const cars = [
    { name: 'Ferrari FF', horsepower: 660, dollar_value: 700000, in_stock: true },
    { name: 'Spyker C12 Zagato', horsepower: 650, dollar_value: 648000, in_stock: false },
    { name: 'Jaguar XKR-S', horsepower: 550, dollar_value: 132000, in_stock: false },
    { name: 'Audi R8', horsepower: 525, dollar_value: 114200, in_stock: false },
    { name: 'Aston Martin One-77', horsepower: 750, dollar_value: 1850000, in_stock: true },
    { name: 'Pagani Huayra', horsepower: 700, dollar_value: 1300000, in_stock: false },
]
const isLastInStock = fp.flowRight(fp.prop('in_stock'), _.last)
console.log(isLastInStock(cars))

const getFirstCarName = fp.flowRight(fp.prop('name'), fp.first)
console.log(getFirstCarName(cars))

const _average = xs => fp.reduce(fp.add, 0, xs) / xs.length
const averageDollarValue = fp.flowRight(_average, fp.map(x => x.dollar_value))
console.log(averageDollarValue(cars))

const _underscore = fp.replace(/\W+/g, '_')
const sanitizeNames = fp.map(fp.flowRight(_underscore, fp.lowerCase, x=> x.name))
console.log(sanitizeNames(cars))

// 三
class Container {
    static of (value) {
        return new Container(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return Container.of(fn(this._value))
    }
}

class Maybe {
    static of(x) {
        return new Maybe(x)
    }

    isNothing() {
        return this._value === null || this._value === undefined
    }

    constructor(x) {
        this._value = x
    }

    map(fn) {
        return this.isNothing() ? this : Maybe.of(fn(this._value))
    }
}

let maybe = Maybe.of([5, 6, 1])
let ex1 = (maybe) => {
    return maybe.map(fp.map(fp.add(1)))
}
console.log(ex1(maybe))

let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = (xs) => {
    return xs.map(fp.first)
}
console.log(ex2(xs))

let safeProp = fp.curry((x, o) => Maybe.of(x[o]))
let user = { id: 2, name: 'Albert' }
let ex3 = (user) => {
    return safeProp(user, 'name').map(fp.first)
}
console.log(ex3(user))

let ex4 = (n) => {
    return Maybe.of(n).map(parseInt)
}
console.log(ex4('100'))
console.log(ex4())

// 四
class MyPromise {
    _status;
    _value;
    _fulfilledCallbacks = [];
    _rejectedCallbacks = [];

    static STATUS_PENDING = 'pending';
    static STATUS_FULFILLED = 'fulfilled';
    static STATUS_REJECTED = 'rejected';

    static resolve(v) {
        if (v instanceof MyPromise) {
            return v
        }
        return new Promise((resolve, reject) => {
            resolve(v)
        })
    }

    static all(vals = []) {
        let result = []
        let count = 0
        return new Promise((resolve, reject) => {
            for (let i = 0; i < vals.length; i++) {
                let v = vals[i];
                if (v instanceof MyPromise) {
                    v.then(r => {
                        result[i] = r
                        count++
                        checkResult()
                    }, r => {
                        reject(r)
                    })
                } else {
                    result[i] = v
                    count++
                }
            }
            checkResult()

            function checkResult() {
                if(count >= vals.length) {
                    resolve(result)
                }
            }
        })
    }

    constructor(fn) {
        this._status = MyPromise.STATUS_PENDING
        fn = typeof fn === typeof Function ? fn : (resolve, reject) => {}
        fn(this.resolve, this.reject)
    }
    resolve = (v) => {
        if (this._status !== MyPromise.STATUS_PENDING) { return }
        setTimeout(() => {
            this._status = MyPromise.STATUS_FULFILLED
            this._value = v
            while(this._fulfilledCallbacks.length > 0) {
                this._fulfilledCallbacks.shift()(v)
            }
        })
    }
    reject = (v) => {
        if (this._status !== MyPromise.STATUS_PENDING) { return }
        setTimeout(() => {
            this._status = MyPromise.STATUS_REJECTED
            this._value = v
            while(this._rejectedCallbacks.length > 0) {
                this._rejectedCallbacks.shift()(v)
            }   
        });
    }
    then(fulfilledCallback, rejectedCallback) {
        fulfilledCallback = typeof fulfilledCallback === typeof Function ? fulfilledCallback : v => v
        rejectedCallback = typeof rejectedCallback === typeof Function ? rejectedCallback : v => v
        return new MyPromise((resolve, reject) => {
            this._fulfilledCallbacks.push(() => {
                try {
                    let v = fulfilledCallback(this._value)
                    if (v instanceof MyPromise) {
                        v.then(resolve, reject)
                    } else {
                        resolve(v)
                    }
                } catch (e) {
                    reject(e)
                }
            })
            this._rejectedCallbacks.push(() => {
                try {
                    let v = rejectedCallback(this._value)
                    if (v instanceof MyPromise) {
                        v.then(resolve, reject)
                    } else {
                        resolve(v)
                    }
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
    catch(fn) {
        return this.then(undefined, fn)
    }
}

let p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000)
})

p.then(v => v+1).then(console.log)
p.then(v => v+2).then(console.log)

MyPromise.all([p, p]).catch(console.log, console.log)
